﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;

namespace System
{
    /// <summary>
    /// 基类型扩展方法
    /// </summary>
    public static class ObjectEx
    {
        /// <summary>
        /// 转字符串
        /// <para>避免null无法ToString</para>
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="nullshow"></param>
        /// <returns></returns>
        public static string NoNullToString(this object obj, string nullshow = "")
        {
            string result = nullshow;
            if (obj != null)
            {
                result = obj.ToString();
            }
            return result;
        }
        /// <summary>
        /// 把对象类型转化为指定类型，转化失败时返回该类型默认值
        /// </summary>
        /// <typeparam name="T"> 动态类型 </typeparam>
        /// <param name="value"> 要转化的源对象 </param>
        /// <returns> 转化后的指定类型的对象，转化失败返回类型的默认值 </returns>
        public static T CastTo<T>(this object value)
        {
            object result;
            Type type = typeof(T);
            try
            {
                if (type.IsEnum)
                {
                    result = Enum.Parse(type, value.ToString());
                }
                else if (type == typeof(Guid))
                {
                    result = Guid.Parse(value.ToString());
                }
                else
                {
                    result = Convert.ChangeType(value, type);
                }
            }
            catch
            {
                result = default(T);
            }

            return (T)result;
        }

        /// <summary>
        /// 把对象类型转化为指定类型，转化失败时返回指定的默认值
        /// </summary>
        /// <typeparam name="T"> 动态类型 </typeparam>
        /// <param name="value"> 要转化的源对象 </param>
        /// <param name="defaultValue"> 转化失败返回的指定默认值 </param>
        /// <returns> 转化后的指定类型对象，转化失败时返回指定的默认值 </returns>
        public static T CastTo<T>(this object value, T defaultValue)
        {
            object result;
            Type type = typeof(T);
            try
            {
                if (type.IsEnum)
                {
                    result = Enum.Parse(type, value.ToString());
                }
                else if (type == typeof(Guid))
                {
                    result = Guid.Parse(value.ToString());
                }
                else
                {
                    result = Convert.ChangeType(value, type);
                }
            }
            catch
            {
                result = defaultValue;
            }
            return (T)result;
        }
        /// <summary>
        /// 通过二进制完成Clone
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T CloneByBinary<T>(this T obj)
        {
            object retval;
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();
                // 序列化成流
                bf.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                // 反序列化成对象
                retval = bf.Deserialize(ms);
                ms.Close();
            }
            return (T)retval;
        }
        /// <summary>
        /// 通过Json方式完成Clone
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T CloneByJson<T>(this T obj)
        {
            // 序列化
            string json = JsonConvert.SerializeObject(obj);
            // 反序列化
            return JsonConvert.DeserializeObject<T>(json);
        }
        /// <summary>
        /// 通过Json方式完成Clone
        /// </summary>
        /// <typeparam name="T">源类型</typeparam>
        /// <typeparam name="T2">目标类型</typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T2 CloneByJson<T, T2>(this T obj)
        {
            // 序列化
            string json = JsonConvert.SerializeObject(obj);
            // 反序列化
            return JsonConvert.DeserializeObject<T2>(json);
        }
        /// <summary>
        /// 迁移同名同类型非列表属性值
        /// </summary>
        /// <typeparam name="T1">源类型</typeparam>
        /// <typeparam name="T2">目标类型</typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="IgnoreCase">是否忽略属性名大小写</param>
        /// <returns></returns>
        public static T2 MergeObjectValue<T1, T2>(T1 source, T2 target, bool IgnoreCase = false)
        {
            var st = source.GetType();
            var tt = target.GetType();
            //只处理值类型和string类型
            var stps = st.GetProperties().Where(p => p.CanWrite == true && !p.PropertyType.IsArray).ToList();
            var ttps = tt.GetProperties().Where(p => p.CanWrite == true && !p.PropertyType.IsArray).ToList();
            foreach (var item in ttps)
            {
                PropertyInfo tp = item;
                var _sps = stps;
                if (IgnoreCase)
                {
                    //全小写比对
                    _sps = stps.Where(p => p.Name.ToLower() == tp.Name.ToLower()).ToList();
                }
                PropertyInfo sp = _sps.FirstOrDefault();
                if (tp != null && sp != null && sp.GetValue(source, null) != null)
                {
                    bool canSet = true;
                    if (tp.PropertyType.Name != sp.PropertyType.Name)
                    {
                        canSet = false;
                        if (tp.PropertyType.IsGenericType)
                        {
                            var definition = tp.PropertyType.GetGenericTypeDefinition();

                            if (definition != null && definition == typeof(Nullable<>)
                                && tp.PropertyType.GetGenericArguments()[0].Name == sp.PropertyType.Name)
                            {
                                //可空泛型，类型转可空可转
                                canSet = true;
                            }
                        }
                    }
                    if (canSet)
                    {
                        var sv = sp.GetValue(source, null);
                        if (tp.PropertyType.FullName == "System.DateTime" && Convert.ToDateTime(sv) < Convert.ToDateTime("1754-01-01")) { continue; }
                        tp.SetValue(target, sv, null);
                    }
                }
            }
            return target;
        }
        /// <summary>
        /// 比对属性
        /// </summary>
        /// <param name="st"></param>
        /// <param name="tt"></param>
        /// <returns></returns>
        public static List<Tuple<string, int, string>> ComparisonPropertyInfo(Type st, Type tt)
        {
            List<Tuple<string, int, string>> result = new List<Tuple<string, int, string>>();

            var stps = st.GetProperties().Where(p => p.CanWrite == true).ToList();
            var ttps = tt.GetProperties().Where(p => p.CanWrite == true).ToList();
            //result.Add(new Tuple<string, int, string>(st.Name, -99, tt.Name));
            foreach (var item in ttps)
            {
                PropertyInfo tp = item;
                string m1 = tp.Name, m3 = "";
                int m2 = -100;
                //全小写比对
                var _sps = stps.Where(p => p.Name.ToLower() == tp.Name.ToLower()).ToList();
                PropertyInfo sp = _sps.FirstOrDefault();
                if (sp == null)
                {
                    m2 = -1;
                    m3 = "未找到匹配";
                }
                else
                {
                    if (sp.Name == item.Name)
                    {
                        if (sp.PropertyType.Name == item.PropertyType.Name)
                        {
                            m2 = 0;
                            m3 = "完全匹配";
                        }
                        else
                        {
                            m2 = 1;
                            m3 = $"名称完全匹配，{item.PropertyType.Name}类型{sp.PropertyType.Name}不匹配";
                        }
                    }
                    else
                    {
                        m2 = 2;
                        m3 = $"{tp.Name}名称差异匹配{sp.Name}";
                        if (sp.PropertyType.Name != item.PropertyType.Name)
                        {
                            m3 += $";{item.PropertyType.Name}类型{sp.PropertyType.Name}不匹配";
                        }
                    }
                    if (_sps.Count > 1)
                    {
                        m3 += $";匹配 {_sps.Count} 个";
                    }
                }

                result.Add(new Tuple<string, int, string>(m1, m2, m3));
            }
            return result;

        }
    }
}
